#include "Util.h"
#include "hpdf.h"
#include <sstream>
#include <iostream>
using namespace std;
/*
Classe Util, utilizada para criacao de funcoes que serao usadas em diversas classes do sistema.
*/

/*
Construtor default.
*/
Util::Util()
{
}

/*
Destrutor default.
*/
Util::~Util()
{
}

jmp_buf env;

#ifdef HPDF_DLL
void  __stdcall
#else
void
#endif
error_handler (HPDF_STATUS   error_no,
               HPDF_STATUS   detail_no,
               void         *user_data)
{
    printf ("ERROR: error_no=%04X, detail_no=%u\n", (HPDF_UINT)error_no,
                (HPDF_UINT)detail_no);
    longjmp(env, 1);
}

/*
Cria um novo documento pdf, o qual sera usado pelo resto do programa para manipulacao das propriedades da biblioteca libharu, que ira gerar o arquivo resultante do processamento realizado.
*/
HPDF_Doc Util::initPDF(){
	HPDF_Doc  pdf;
    pdf = HPDF_New (error_handler, NULL);
    if (!pdf) {
        printf ("error: cannot create PdfDoc object\n");
    }
    if (setjmp(env)) {
        HPDF_Free (pdf);
    }
    HPDF_SetCompressionMode (pdf, HPDF_COMP_ALL); 
	return pdf;
}

/*
Cria um novo objeto do tipo HPDF_Page, uma pagina no formato libharu.
*/
HPDF_Page Util::initPage(HPDF_Doc pdf, Pagina pag){
    HPDF_Page page;
    page = HPDF_AddPage(pdf);
	HPDF_Page_SetSize(page, HPDF_PageSizes(pag.getDimensao()), HPDF_PageDirection(pag.getDirecao()));
	return page;
}

/*
Funcao usada para salvar o arquivo pdf, recebe o documento pdf a ser salvo e o nome para o arquivo.
*/
void Util::savePDF(HPDF_Doc pdf, string fileName)
{
	fileName.append(".pdf");
	HPDF_SaveToFile (pdf, fileName.c_str());
    HPDF_Free (pdf);
}

/*
Funcao utilizada para carregar uma imagem para o formato usado na libharu. Recebe uma Imagem e um Documento HPDF como parametros e retorna uma Imagem HPDF.
*/
HPDF_Image Util::loadImage(HPDF_Doc pdf, Imagem image)
{
	HPDF_Image img;
	string aux = image.getPath();
	if(aux.find_first_of(".jpg")!= string::npos)
	{
		img = HPDF_LoadJpegImageFromFile(pdf, image.getPath().c_str());
	} else if (aux.find_first_of(".png")!= string::npos){
		img = HPDF_LoadPngImageFromFile(pdf, image.getPath().c_str());
	} else {
		cout<<"O arquivo informado nao pode ser carregado..."<<endl;
	}
	return img;
}

/*
	Funcao utilizada para carregar uma imagem para o formato usado na libharu. 
Recebe o nome da imagem a ser carregada e um Documento HPDF como parametros 
e retorna uma Imagem HPDF.
*/
HPDF_Image Util::loadImage2(HPDF_Doc pdf, string name){
	HPDF_Image img;
	string aux = name;
	if(aux.find_first_of(".jpg")!= string::npos)
	{
		img = HPDF_LoadJpegImageFromFile(pdf, name.c_str());
	} else if (aux.find_first_of(".png")!= string::npos){
		img = HPDF_LoadPngImageFromFile(pdf, name.c_str());
	} else {
		cout<<"O arquivo informado nao pode ser carregado..."<<endl;
	}
	return img;
}

/*
Funcao que retorna um vetor de strings correspondente aos arquivos contidos no diretorio passado como parametro.
path - o caminho do diretorio do qual sera listado todos os seus arquivos.
*/
vector<string> Util::getNames(string path){
	string command("dir " + path + " /B /S /A:-D-H > dir.txt");
    vector<string> fileNamesVector;

    system(command.c_str());
    ifstream fin("dir.txt");
    if(fin.fail()){
		cout<<" \n Error opening file dir.txt. \n";
    }else{
		string currentLine;
		getline(fin, currentLine);
        while(!fin.eof() && !currentLine.empty()){
			fileNamesVector.push_back(currentLine);
			getline(fin, currentLine);
		}
	}
	fin.close();    
	command = "del dir.txt";
    return fileNamesVector;

}

/*
Funcao que retorna um vetor de Imagens a partir do diretorio que contem as imagens. Utiliza tambem um Documento pdf como parametro que sera usado para construcao das imagens da biblioteca libharu.
pathImages - o diretorio que contem as imagens que serao usadas para construcao do vetor.
*/
vector<Imagem> Util::getImages(HPDF_Doc pdf, string pathImages)
{
	vector<Imagem> imagens;
	HPDF_Image image;
	vector<string> path = getNames(pathImages);
	for (int i = 0; i < path.size(); i++){
		Imagem img;
		img.setAreaProportion(path.size()-i);
		
		img.setPahtImage(path.at(i));
		img.setHpdfImage(loadImage(pdf, img));
		imagens.push_back(img);
	}
	return imagens;
}

/*
Funcao que calcula e retorna o maior dentre dois valores.
*/
double Util::max(double a, double b)
{
	if(a>b) return a;
	return b;
}

/*
Funcao que calcula e retorna o menor dentre dois valores.
*/
double Util::min(double a, double b)
{
	if(a<b) return a;
	return b;
}

/*
Funcao usada para calcular o aspect ratio das caixas delimitadoras que sao cortes horizontais - possuem os nodos filhos um sobre o outro.
*/
double Util::aspectRatioSideBySide(double ar, double al, double er, double el)
{

	double a;
	if (ar == 0) // Tratamento para o caso de uma unica foto e que esteja no lado esquerdo da arvore.
	{
		a = sqrt(al*el)/sqrt(el/al);
	}else if(al == 0){ // Tratamento para o caso de uma unica foto e que esteja no lado direito da arvore.
		a = sqrt(ar*er)/(sqrt(er/ar));

	} else a = sqrt(max(ar*er, al*el))/(sqrt(el/al) + sqrt(er/ar));
	return a;
}

/*
Funcao usada para calcular o aspect ratio das caixas delimitadoras que sao cortes horizontais - possuem os nodos filhos um sobre o outro.
*/
double Util::aspectRatioTop(double ar, double al, double er, double el)
{
	double a;
	if (ar == 0){ // Tratamento para o caso de uma unica foto e que esteja no lado esquerdo da arvore.
		a = (sqrt(el*al))/(sqrt(el/al));
	}else if(al == 0){ // Tratamento para o caso de uma unica foto e que esteja no lado direito da arvore.
		a = (sqrt(er*ar))/(sqrt(er/ar));
	} else a = (sqrt(el*al) + sqrt(er*ar))/(sqrt(max(er/ar, el/al)));
	return a;
}

/*
Funcao usada para o calculo da area proportion de caixas delimitadoras que sao cortes verticais - seus filhos sao colocados lado a lado.
*/
double Util::areaProportionSideBySide(double ar, double al, double er, double el)
{
	double e;
	// Tratamento para o caso de uma unica foto e que esteja no lado esquerdo da arvore.
	if (ar == 0) 
	{
		e = sqrt(al*el)*(sqrt(el/al));
	}
	else if (al == 0) // Tratamento para o caso de uma unica foto e que esteja no lado direito da arvore.
	{
		e = sqrt(ar*er)*sqrt(er/ar);
	} 
	else e = sqrt(max(ar*er, al*el))*(sqrt(el/al) + sqrt(er/ar));
	return e;
}
/*
Funcao usada para o calculo da area proportion de caixas delimitadoras que sao cortes horizontais - seus filhos sao colocados um sobre o outro.
*/
double Util::areaProportionTop(double ar, double al, double er, double el)
{
	double e;
	// Tratamento para o caso de uma unica foto e que esteja no lado esquerdo da arvore.
	if (ar == 0) 
	{ 
		e = sqrt(el/al)*sqrt(el*al);
	} else if(al == 0) // Tratamento para o caso de uma unica foto e que esteja no lado direito da arvore.
	{
		e = sqrt(er/ar)*sqrt(er*ar);
	} else e = (sqrt(max(er/ar, el/al)))*(sqrt(el*al) + sqrt(er*ar));
	return e;
}

/*
Dados a altura e a largura da pagina em pixels, retorna sua area em centimetros.
*/
double Util::areaPagina(double hPagina, double wPagina)
{
	return hPagina*wPagina;
}

/*
Calcula a area da caixa delimitadora principal, utilizando para isso a area da pagina (areaPagina), o aspect ratio da pagina (aPagina) e o aspect ratio da caixa (aBB).
*/
double Util::areaBB(double areaPagina, double aPagina, double aBB)
{
	double area;
	area = areaPagina*(min(aBB, aPagina)/max(aBB, aPagina));
	return area;
}

/*
Calcula a area da foto de acordo com a area da caixa delimitadora principal (areaPBB), a proporcao da foto (eFoto) e a proporcao da caixa (ePBB).
Utiliza a constante G descrita no artigo como sendo 0.95^2 que pode ser usada para criar bordas ao redor da fotografia.
*/
double Util::areaFoto(double eFoto, double ePBB, double areaPBB)
{
	double areaF;
	areaF = G*(eFoto/ePBB)*areaPBB;
	return areaF;
}
/*
Funcao utilizada para calcular a altura e largura que a foto tera no arquivo pdf.
*/
vector<double> Util::alturaLarguraFoto(double areaF, double aspectRatioFoto)
{
	vector<double> hW;
	double height, width;
	height = sqrt(areaF/aspectRatioFoto);
	width = aspectRatioFoto*height;
	hW.push_back(height);
	hW.push_back(width);
	return hW;
}
/*
Calcula o aspect ratio da fotografia a partir da sua altura e largura.
*/
double Util::aspectRatioFoto(double width, double height)
{
	return width/height;
}

/*
Funcao que calcula a posicao no eixo x das folhas filhas de uma caixa divisoria vertical.
posX retorna valores no intervalo (0, 1). 0 representando a posicao mais a esquerda e 1 a mais a direita.
*/
double Util::posX(double el, double al, double er, double ar)
{
	double xValue;
	//Modificacao realizada para o caso de haver apenas uma imagem a ser inserida no pdf.
	if(ar == 0) {
		xValue = 1;
	} else xValue = sqrt(el/al)/(sqrt(el/al) + sqrt(er/ar));
	return xValue;
}

/*
Funcao que calcula a posicao no eixo y das folhas filhas de uma caixa divisoria horizontal. 
posY retorna valores no intervalo (0, 1). 0 representando a posicao mais no piso (bottom) e 1 a mais no topo (top).
*/
double Util::posY(double eb, double ab, double et, double at)
{
	double yValue;
	yValue = sqrt(eb*ab)/(sqrt(eb*ab) + sqrt(et*at));
	return yValue;
}

/*
	Funcao que recebe um vetor de Imagens como parametro e um nome e retorna a imagem correspondente caso exista uma imagem do vetor com o nome passado como 
	parametro, do contrario, NULL e retornado indicando que nao ha imagem com o referido nome no vetor de entrada.
*/
Imagem Util::getImagemPorNome(vector<Imagem> im, string name)
{
    Imagem imagem;
	for(int i=0; i<im.size(); i++)
	{
		// retorna a imagem cujo nome foi passado como argumento
		if(im.at(i).getPath() == name)
		{
			imagem = im.at(i);
                        break;
		}
	}
	// caso nao encontre nenhuma imagem com nome correspondente.
	return imagem;
}

/*
	Funcao usada para calcular a altura de um objeto de acordo com sua largura e seu aspect ratio.
*/
double Util::getAlturaByLargura(double aspect, double largura){
	return largura/aspect;
}

/*
	Funcao usada para calcular a largura de um objeto de acordo com sua altura e seu aspect ratio.
*/
double Util::getLarguraByAltura(double aspect, double altura){
	return altura*aspect;
}

/*
	Funcao usada para ler umm arquivo de configuracoes para o sistema, 
	tal arquivo possui a seguinte estrutura: 
	path da imagem,importancia,legenda para a imagem.
*/
vector<string> Util::readFile(string pathFile){
	vector<string> fileContent;
	ifstream arq(pathFile.data());
	if(arq.fail()){
		cout<<"\n Unable to open file. \n";
	}else{
		string currentLine;
		getline(arq, currentLine);
		while(!arq.eof() && !currentLine.empty()){
			fileContent.push_back(currentLine);
			getline(arq, currentLine);
		}
	}
	arq.close();
	return fileContent;
}

/*
Funcao que retorna um vetor de Imagens a partir do diretorio que contem as imagens. 
Utiliza tambem um Documento pdf como parametro que sera usado para construcao das 
imagens da biblioteca libharu. pathImages - o vetor que contem os nomes das imagens 
que serao usadas para construcao do vetor de imagens.
*/
vector<Imagem> Util::getImages2(HPDF_Doc pdf, vector<string> pathImages) {
	HPDF_Image image;
	vector<Imagem> imagens;
	for (int i = 0; i < pathImages.size(); i++){
		Imagem img;
		img.setAreaProportion(pathImages.size()-i);
		img.setPahtImage(pathImages.at(i));
		img.setHpdfImage(loadImage(pdf, img));
                img.setArea(HPDF_Image_GetWidth(img.getHpdfImage())*HPDF_Image_GetHeight(img.getHpdfImage()));
                img.setAspectRatio(HPDF_Image_GetWidth(img.getHpdfImage())/HPDF_Image_GetHeight(img.getHpdfImage()));
//                cout << pathImages.at(i) << " " << HPDF_Image_GetWidth(img.getHpdfImage()) << " " << HPDF_Image_GetHeight(img.getHpdfImage()) << endl;
		imagens.push_back(img);
	}
	return imagens;
}

/*
	Funcao usada para realizar a chamada ao modulo de corte de imagens AutoCropp.
*/
string Util::getCropp(string path, double aspect){
	std::stringstream s0;
	s0 << aspect;

	string command("AutoCrop.exe ");
	command.append(path);
	command.append(" ");
	command.append(s0.str());
	system(command.c_str());

	return path.substr(0, path.find_last_of(".")).append("-Cropp.jpg");
}


vector<Imagem> Util::getImages3(HPDF_Doc pdf, vector<string> pathImages, vector<int> importancia) {
	HPDF_Image image;
	vector<Imagem> imagens;
	for (int i = 0; i < pathImages.size(); i++){
		Imagem img;
		img.setAreaProportion(pathImages.size()-i);		
		img.setPahtImage(pathImages.at(i));
		img.setHpdfImage(loadImage(pdf, img));
                img.setOrdem(importancia.at(i));
		imagens.push_back(img);
	}
	return imagens;
}

vector<Imagem> Util::loadImageList(string fileName, HPDF_Doc pdf) {
    vector<Imagem> imagens;
    fstream file(fileName.c_str(), ios::in);
    string line, buffer, path, description, sPriority;
    int priority;
    int i = 0;
    if(!file.fail()) {
        while(file >> line) {
            istringstream ss(line);
            getline(ss, path, ',');
            getline(ss, sPriority, ',');
            getline(ss, description);
            istringstream zz(sPriority);
            zz >> priority;
            Imagem image(path);
            image.setOrdem(priority);
            image.setConteudo(description);
            image.setHpdfImage(loadImage(pdf, image));
            image.setId(i++);
            image.setArea(HPDF_Image_GetWidth(image.getHpdfImage())*HPDF_Image_GetHeight(image.getHpdfImage()));
            image.setAspectRatio(HPDF_Image_GetWidth(image.getHpdfImage())/HPDF_Image_GetHeight(image.getHpdfImage()));
            imagens.push_back(image);
        }
    }
    return imagens;
}
